package com.study.doc.search;

import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

public class DoSearch {

    //停用词表的路径
    private final String STOP_PATH = "D:/JAVA/jdk-8u311-docs-all/stops.txt";
    //存放停用词
    private Set<String> stopWords = new HashSet<>();
    Index index = new Index();

    public DoSearch(){
        //加载索引
        index.load();
        //加载停用词
        loadStopWords();
    }

    public List<Result> Search(String query){
        //[分词并过滤停顿词]
        List<Term> tmpTerms = ToAnalysis.parse(query).getTerms();
        List<Term> terms = new ArrayList<>();
        for (Term tmpTerm : tmpTerms) {
            if (stopWords.contains(tmpTerm.getName())){
                continue;
            }
            terms.add(tmpTerm);
        }
        //[查询倒排索引并存储结果]
        List<List<Weight>> result = new ArrayList<>();
        for (Term term : terms) {
            String word = term.getName();
            List<Weight> weights = index.getWeights(word);
            if (weights == null){
                continue;
            }
            result.add(weights);
        }

        //【将查出来的相同文档进行权重合并】
        List<Weight> allTermsResult = merge(result);

        //【将合并后的结果按照权重降序排序】
        allTermsResult.sort((o1,o2)->o2.getWeight()-o1.getWeight());
        
        //【包装结果】
        List<Result> results = new ArrayList<>();
        for (Weight weight : allTermsResult) {
            DocInfo docInfo = index.getDoc(weight.docId);
            Result resultDoc = new Result();
            resultDoc.setTitle(docInfo.getTitle());
            resultDoc.setUrl(docInfo.getUrl());
            resultDoc.setDesc(GenDesc(docInfo.getContent(),terms));
            results.add(resultDoc);
        }
        return results;
    }

    private String GenDesc(String content, List<Term> terms) {
        // 先遍历分词结果, 看看哪个结果是在 content 中存在.
        int firstPos = -1;
        for (Term term : terms) {
            String word = term.getName();
            content = content.toLowerCase().replaceAll("\\b" + word + "\\b", " " + word + " ");
            firstPos = content.indexOf(" " + word + " ");
            if (firstPos >= 0) {
                // 找到了位置
                break;
            }
        }
        // 后面还需要用到这个 firstPos
        if (firstPos == -1) {
            // 所有的分词结果都不在正文中存在.
            // 直接取正文的前 160 个字符作为描述.
            if (content.length() > 160) {
                return content.substring(0, 160) + "...";
            }
            return content;
        }
        // 从 firstPos 作为基准位置, 往前找 60 个字符, 作为描述的起始位置.
        String desc = "";
        int descBeg = firstPos < 60 ? 0 : firstPos - 60;
        if (descBeg + 160 > content.length()) {
            desc = content.substring(descBeg);
        } else {
            desc = content.substring(descBeg, descBeg + 160) + "...";
        }

        // 在此处加上一个替换操作. 把描述中的和分词结果相同的部分, 给加上一层 <i> 标签. 就可以通过 replace 的方式来实现.
        for (Term term : terms) {
            String word = term.getName();
            // 注意, 此处要进行全字匹配. 也就是当查询词为 List 的时候 不能把 ArrayList 中的 List 给单独标红
            desc = desc.replaceAll("(?i) " + word + " ", "<i> " + word + " </i>");
        }
        return desc;
    }

    private List<Weight> merge(List<List<Weight>> result){
        class Pos{
            int row;
            int col;
            public Pos(int row, int col) {
                this.row = row;
                this.col = col;
            }
        }
        PriorityQueue<Pos> priorityQueue = new PriorityQueue<>((o1,o2)->{
            Weight weight1 = result.get(o1.row).get(o1.col);
            Weight weight2 = result.get(o2.row).get(o2.col);
            return weight1.getDocId()-weight2.getDocId();
        });
        //把结果中的每一个List的第一个元素下标放入优先级队列中
        for (int i=0; i<result.size(); ++i){
            priorityQueue.offer(new Pos(i,0));
        }
        List<Weight> ans = new ArrayList<>();
        //循环取队首元素
        while (!priorityQueue.isEmpty()){
            Pos pos = priorityQueue.poll();
            Weight curWeight = result.get(pos.row).get(pos.col);
            //查看当前元素的docId是否与上一个相同
            if (ans.size() > 0){
                Weight preWeight = ans.get(ans.size()-1);
                //如果相同就合并
                if (preWeight.getDocId() == curWeight.getDocId()){
                    preWeight.setWeight(preWeight.getWeight()+curWeight.getWeight());
                }else{
                    //不同就直接加入结果集
                    ans.add(curWeight);
                }
            }else{
                ans.add(curWeight);
            }
            //插入操作完成后，指针往后移一位
            Pos newPos = new Pos(pos.row,pos.col+1);
            if (newPos.col >= result.get(newPos.row).size()){
                continue;
            }
            priorityQueue.offer(newPos);
        }
        return ans;
    }

    //加载停用词
    public void loadStopWords(){
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(STOP_PATH))){
            while (true){
                String word = bufferedReader.readLine();
                if (word == null){
                    break;
                }
                stopWords.add(word);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        DoSearch doSearch = new DoSearch();
        List<Result> res =  doSearch.Search("arraylist");
        for (Result result : res) {
            System.out.println(result.title);
        }
    }
}
